package bocchi.general;

import com.megacrit.cardcrawl.actions.AbstractGameAction;
import com.megacrit.cardcrawl.actions.AbstractGameAction.AttackEffect;
import com.megacrit.cardcrawl.actions.common.*;
import com.megacrit.cardcrawl.cards.AbstractCard;
import com.megacrit.cardcrawl.cards.CardGroup;
import com.megacrit.cardcrawl.cards.DamageInfo;
import com.megacrit.cardcrawl.cards.DamageInfo.DamageType;
import com.megacrit.cardcrawl.characters.AbstractPlayer;
import com.megacrit.cardcrawl.core.AbstractCreature;
import com.megacrit.cardcrawl.dungeons.AbstractDungeon;
import com.megacrit.cardcrawl.powers.AbstractPower;

import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiConsumer;

public class Util {
    private static final DamageType DEFAULT_DAMAGE_TYPE = DamageType.NORMAL;
    private static final AttackEffect DEFAULT_ATTACK_EFFECT = AttackEffect.FIRE;

    public static void addToBottom(AbstractGameAction action) {
        AbstractDungeon.actionManager.addToBottom(action);
    }

    public static void addToTop(AbstractGameAction action) {
        AbstractDungeon.actionManager.addToTop(action);
    }

    public static void damage(AbstractCreature source, AbstractCreature target, int damage, DamageType type, AttackEffect effect) {
        addToBottom(new DamageAction(target, new DamageInfo(source, damage, type), effect));
    }

    public static void damage(AbstractCreature source, AbstractCreature target, int damage, DamageType type) {
        damage(source, target, damage, type, DEFAULT_ATTACK_EFFECT);
    }

    public static void damage(AbstractCreature source, AbstractCreature target, int damage) {
        damage(source, target, damage, DEFAULT_DAMAGE_TYPE);
    }

    public static void aoe(AbstractPlayer player, int damage, DamageType type, AttackEffect effect) {
        addToBottom(new DamageAllEnemiesAction(player, damage, type, effect));
    }

    public static void aoe(AbstractPlayer player, int damage, DamageType type) {
        aoe(player, damage, type, DEFAULT_ATTACK_EFFECT);
    }

    public static void aoe(AbstractPlayer player, int damage) {
        aoe(player, damage, DEFAULT_DAMAGE_TYPE);
    }

    public static void randomDamage(AbstractCreature source, int damage, DamageType type, AttackEffect effect) {
        addToBottom(new DamageRandomEnemyAction(new DamageInfo(source, damage, type), effect));
    }

    public static void randomDamage(AbstractCreature source, int damage, DamageType type) {
        randomDamage(source, damage, type, DEFAULT_ATTACK_EFFECT);
    }

    public static void randomDamage(AbstractCreature source, int damage) {
        randomDamage(source, damage, DEFAULT_DAMAGE_TYPE);
    }

    public static void applyPower(AbstractCreature source, AbstractCreature target, AbstractPower power, AttackEffect effect) {
        addToBottom(new ApplyPowerAction(target, source, power, power.amount, effect));
    }

    public static void applyPower(AbstractCreature source, AbstractCreature target, AbstractPower power) {
        applyPower(source, target, power, DEFAULT_ATTACK_EFFECT);
    }

    public static void block(AbstractCreature target, int amount) {
        addToBottom(new GainBlockAction(target, amount));
    }

    public static void iterateCards(BiConsumer<AbstractCard, CardGroup> action) {
        AbstractPlayer player = AbstractDungeon.player;
        for (AbstractCard card : player.hand.group) {
            action.accept(card, player.hand);
        }

        for (AbstractCard card : player.drawPile.group) {
            action.accept(card, player.drawPile);
        }

        for (AbstractCard card : player.discardPile.group) {
            action.accept(card, player.discardPile);
        }

        for (AbstractCard card : player.exhaustPile.group) {
            action.accept(card, player.exhaustPile);
        }
    }

    public static CardGroup findCard(UUID id) {
        AtomicReference<CardGroup> cardGroup = new AtomicReference<>();

        iterateCards((card, group) -> {
            if (card.uuid.equals(id)) {
                cardGroup.set(group);
            }
        });

        return cardGroup.get();
    }
}
